package com.netty.model;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.netty.constant.Constants;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;

import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author suzhen
 * @create 2018/7/16
 */
@Getter
@Setter
@Slf4j
public class SocketSession implements BaseModelAble {

    private static final long serialVersionUID = 8269505210699191257L;
    private transient Channel session;
    private ScriptSession dwrSession;
    /**
     * session在本台服务器上的ID
     */
    private String sessionId;
    /**
     * 来源 用于区分是websocket\socket\dwr
     */
    private int source;
    //客户端ID  (设备号码+应用包名),ios为devicetoken
    private String deviceId;
    //session绑定的服务器IP
    private String host;
    //session绑定的账号
    private String account;
    //终端类型
    private String platform;
    //终端版本号
    private String platformVersion;
    //客户端key
    private String appKey;
    //登录时间
    private Long bindTime;
    //更新时间
    private Long updateTime;
    //签名
    private String sign;
    //经度
    private Double longitude;
    //维度
    private Double latitude;
    //位置
    private String location;
    // 状态
    private int status;
    /**
     * 用于dwr websocket存储多开页面创建的session
     * 挡一个账号在多处登录的时候就会储存在这里面
     */
    private Map<String, SocketSession> sessions = new HashMap<>();

    public void addSessions(SocketSession session) {
        sessions.put(session.getSessionId(), session);
    }


    public Long getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Long updateTime) {
        this.updateTime = updateTime;
        setAttribute("updateTime", updateTime);
    }


    public SocketSession(ScriptSession session) {
        this.dwrSession = session;
        this.sessionId = session.getId();
    }

    public SocketSession(Channel session) {
        this.session = session;
        this.sessionId = session.id().asShortText();
    }

    public SocketSession() {
    }


    public void setAccount(String account) {
        this.account = account;
        setAttribute(Constants.SessionConfig.SESSION_KEY, account);
    }

    public void setLongitude(Double longitude) {
        setAttribute("longitude", longitude);
        this.longitude = longitude;
    }

    public void setLatitude(Double latitude) {
        setAttribute("latitude", latitude);
        this.latitude = latitude;
    }


    public void setLocation(String location) {
        setAttribute("location", location);
        this.location = location;
    }


    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
        setAttribute("deviceId", deviceId);
    }

    public void setBindTime(Long bindTime) {
        this.bindTime = bindTime;
        setAttribute("bindTime", bindTime);
    }

    public void setHost(String host) {
        this.host = host;
        setAttribute("host", host);
    }

    public void setChannel(Channel session) {
        this.session = session;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
        setAttribute("status", status);
    }

    public void setSession(Channel session) {
        this.session = session;
    }

    public Channel getSession() {
        return session;
    }

    public List<Channel> getSessionAll() {
        List<Channel> list = new ArrayList<>();
        list.add(getSession());
        for (String key : sessions.keySet()) {
            SocketSession session = sessions.get(key);
            if (session != null) {
                list.add(session.getSession());
            }
        }
        return list;
    }


    public String getPlatform() {
        return platform;
    }

    public void setPlatform(String platform) {
        this.platform = platform;
        setAttribute("platform", platform);
    }

    public String getPlatformVersion() {
        return platformVersion;
    }

    public void setPlatformVersion(String platformVersion) {
        this.platformVersion = platformVersion;
        setAttribute("platformVersion", platformVersion);
    }

    public String getAppKey() {
        return appKey;
    }

    public void setAppKey(String appKey) {
        this.appKey = appKey;
        setAttribute("appKey", appKey);
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
        setAttribute("sign", sign);
    }


    public int getSource() {
        return source;
    }

    public void setSource(int source) {
        this.source = source;
        setAttribute("source", source);
    }

    public void setAttribute(String key, Object value) {
        if (session != null) {
            session.attr(AttributeKey.valueOf(key)).set(value);
        }
    }


    public boolean containsAttribute(String key) {
        if (session != null) {
            return session.hasAttr(AttributeKey.valueOf(key));
        }
        return false;
    }

    public Object getAttribute(String key) {
        if (session != null) {
            return session.attr(AttributeKey.valueOf(key)).get();
        }
        return null;
    }

    public void removeAttribute(String key) {
        if (session != null) {
            session.attr(AttributeKey.valueOf(key)).set(null);
        }
    }

    public SocketAddress getRemoteAddress() {
        if (session != null) {
            return session.remoteAddress();
        }
        return null;
    }

    public boolean write(Object msg) {
        if (sessions.size() > 0) {
            for (String key : sessions.keySet()) {
                SocketSession session = sessions.get(key);
                log.debug("session[i]: {}", session);
                if (session != null) {
                    session.write(msg);
                }
            }
        }
        if (session != null && isConnected()) {
            log.debug("session: {}", msg);
            log.debug("session: {}", session);
            return session.writeAndFlush(msg).awaitUninterruptibly(2000);
        } else if (dwrSession != null && isConnected()) {
            log.debug("dwrsession: {}", msg);
            try {
                Message msgModel = (Message) msg;
                Map<String, Object> map = new HashMap<>(2);
                map.put("user", msgModel.getSender());
                map.put("content", msgModel.getContent());
                ScriptBuffer script = new ScriptBuffer();
                script.appendCall(Constants.DWRConfig.DWR_SCRIPT_FUNCTIONNAME, JSONArray.toJSON(map));
                dwrSession.addScript(script);
                //protobuf 不支持低版本IE 没法使用protobuf  支持可以使用以下方法
				/*MessageProto.Model  msgModel = (MessageProto.Model)msg;
				ScriptBuffer script = new ScriptBuffer();
				Object json = JSONArray.toJSON(msgModel.toByteArray());
				script.appendCall(Constants.DWRConfig.DWR_SCRIPT_FUNCTIONNAME, json);
				dwrsession.addScript(script); */
                return true;
            } catch (Exception ignored) {

            }
        }
        return false;
    }


    public boolean isConnected() {
        //判断是否有多个session
        if (sessions != null && sessions.size() > 0) {
            for (String key : sessions.keySet()) {
                SocketSession session = sessions.get(key);
                if (session != null && session.isConnected()) {
                    return true;
                }
            }
        } else if (session != null) {
            return session.isActive();
        } else if (dwrSession != null) {
            return !dwrSession.isInvalidated();
        }
        return false;
    }

    public boolean isLocalhost() {

        try {
            String ip = InetAddress.getLocalHost().getHostAddress();
            return ip.equals(host);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return false;

    }


    public int otherSessionSize() {
        return sessions.size();
    }


    public void close() {
        if (session != null) {
            session.close();
        } else if (dwrSession != null) {
            dwrSession.invalidate();
        }
    }

    public void closeAll() {
        close();
        for (String key : sessions.keySet()) {
            SocketSession session = sessions.get(key);
            if (session != null) {
                session.close();
                sessions.remove(key);
            }
        }
    }

    public void close(String nid) {
        if (getSessionId().equals(nid)) {
            close();
        } else {
            SocketSession session = sessions.get(nid);
            sessions.remove(nid);
            session.close();
        }
    }


    @Override
    public int hashCode() {
        return (deviceId + sessionId + host).hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof SocketSession) {
            return hashCode() == o.hashCode();
        }
        return false;
    }

    public boolean fromOtherDevice(Object o) {
        if (o instanceof SocketSession) {
            SocketSession t = (SocketSession) o;
            if (t.deviceId != null && deviceId != null) {
                return !t.deviceId.equals(deviceId);
            }
        }
        return false;
    }

    public boolean fromCurrentDevice(Object o) {
        return !fromOtherDevice(o);
    }


    @Override
    public String toString() {
        return JSON.toJSONString(SocketSession.this);
    }

}
